/* eslint-disable jsx-a11y/label-has-associated-control */
import { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
import { Form, useActionData, useFetcher, useNavigation } from "@remix-run/react";
import React from "react";
import { useTranslation } from "react-i18next";
import { z } from "zod";
import { createPermission, deletePermission, findAllPermission, PermissionModel, updatePermission, zPermission } from "~/.server/models/permission";
import { FormFieldError } from "~/components/form_validate";
import { IconSelect } from "~/components/icon_select";
import useNotification, { Notification } from "~/components/notification";
import TreeSelect from "~/components/tree_select";
import { Table } from "~/components/tree_table";
import i18nServer from "~/i18n/i18n.server";
import { PaginationProps, SortProps, TreeNode } from "~/types";
import { findPermission, toTreeNode, filterPermissionByKeyword } from "~/utils";

export async function loader({ request }: LoaderFunctionArgs) {
  const { searchParams } = new URL(request.url);
  const action = searchParams.get("action");
  if (action == "tableData") {
    const column = searchParams.get("column") || undefined;
    const order = searchParams.get("order") || undefined;
    const permissions = await findAllPermission(column, order);
    return { permissions };
  } else if (action === "treeSelectData") {
    const permissions = await findAllPermission();
    return { permissions };
  }
  return {}
}

export default function Permissions() {
  const actionData = useActionData<typeof action>();

  // i18n
  const { t } = useTranslation();
  // navigation
  const navigation = useNavigation();
  const isSubmitting = navigation.formAction === "/admin/permissions";

  const permissionFetcherUrl = "?action=tableData"
  const treeSelectFetcherUrl = "?action=treeSelectData"

  // const [myActionData, setMyActionData] = React.useState<{ code?: number; name?: string[], message?: string }>({});
  const permissionFetcher = useFetcher<typeof loader>();
  const treeSelectFetcher = useFetcher<typeof loader>();
  const [tableData, setTableData] = React.useState<PermissionModel[]>([]);
  // const [treeSelectData, setTreeSelectData] = React.useState<TreeNode[]>([]);
  const [editPermissionInfo, setEditPermissionInfo] = React.useState<Partial<PermissionModel>>();
  const [deletePermissionId, setDeletePermissionId] = React.useState<number>(0);
  const { notifications, addNotification } = useNotification();

  // init table data
  React.useEffect(() => {
    if (permissionFetcher.state === "idle" && permissionFetcher.data && permissionFetcher.data.permissions) {
      setTableData(permissionFetcher.data.permissions);
    }
  }, [permissionFetcher]);
  // init permissionFetcher
  React.useEffect(() => {
    if (permissionFetcher.state === "idle" && !permissionFetcher.data) {
      permissionFetcher.load(permissionFetcherUrl);
    }
  }, [permissionFetcher])
  // init treeSelectFetcher
  React.useEffect(() => {
    if (treeSelectFetcher.state === "idle" && !treeSelectFetcher.data) {
      treeSelectFetcher.load(treeSelectFetcherUrl);
    }
  }, [treeSelectFetcher])
  // init actionData message
  React.useEffect(() => {
    if (actionData?.message) {
      addNotification({ type: actionData?.code === 0 ? "success" : "error", msg: actionData.message });
    }
    if (actionData?.code === 0) {
      closePermissionDialog();
      closeDeletePermissionDialog();
    }
  }, [actionData, addNotification]);
  // 添加一个esc监听，由于dialog默认监听esc关闭窗口，但是并不会清空数据，此处清空数据
  React.useEffect(() => {
    const handleEsc = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        closePermissionDialog();
      }
    };
    document.addEventListener("keydown", handleEsc);
    return () => {
      document.removeEventListener("keydown", handleEsc);
    };
  }, []);

  const handleSearch = (event: React.FormEvent) => {
    // get current value
    const search = (event.target as HTMLInputElement).value;
    setTableData(filterPermissionByKeyword(permissionFetcher.data?.permissions ?? [], search));
  };

  const handleEditPermission = (permissionId: number) => {
    if (permissionId) {
      const permission = findPermission(tableData, permissionId);
      if (permission) {
        setEditPermissionInfo(permission);
        // setTreeSelectData(toTreeNode(tableData.filter(permission => permission.id !== permissionId)));
      }
    }
    const dialogPermission = document.getElementById('dialog_permission') as HTMLDialogElement;
    dialogPermission.showModal();
  };
  const handleOpenPermissionDialog = () => {
    const dialogPermission = document.getElementById('dialog_permission') as HTMLDialogElement;
    dialogPermission.showModal();
  };
  const handleClosePermissionDialog = (event: React.FormEvent) => {
    event.preventDefault();
    // setMyActionData({});
    closePermissionDialog();
  };
  const closePermissionDialog = () => {
    const dialog = document.getElementById('dialog_permission') as HTMLDialogElement;
    dialog.close();
    setEditPermissionInfo({});
  };
  const handleFormChange = (event: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLSelectElement>) => {
    const { name, value } = event.target;
    setEditPermissionInfo({ ...editPermissionInfo, [name]: value });
  };

  const handleTreeSelect = (node: TreeNode) => {
    setEditPermissionInfo({ ...editPermissionInfo, parent_id: Number(node.id) });
  };
  const handleIconSelect = (icon: string) => {
    setEditPermissionInfo({ ...editPermissionInfo, icon: icon });
  };
  const handleOpenDeletePermissionDialog = (permissionId: number) => {
    setDeletePermissionId(permissionId);
    const dialogDeletePermission = document.getElementById('dialog_delete_permission') as HTMLDialogElement;
    dialogDeletePermission.showModal();
  };
  const handleCloseDeletePermissionDialog = (event: React.FormEvent) => {
    event.preventDefault();
    setDeletePermissionId(0);
    closeDeletePermissionDialog();
  };
  const closeDeletePermissionDialog = () => {
    const dialogDeletePermission = document.getElementById('dialog_delete_permission') as HTMLDialogElement;
    dialogDeletePermission.close();
  };

  const onTableChange = (_: PaginationProps, sortProps: SortProps | null) => {
    if (sortProps) {
      permissionFetcher.load(permissionFetcherUrl + `&column=${sortProps.column}&order=${sortProps.order}`);
    } else {
      permissionFetcher.load(permissionFetcherUrl);
    }
  };

  return (
    <>
      <div className="card bg-base-100 shadow-xl">
        <div className="card-body">
          <div className="card-title justify-between">
            <input type="text" placeholder="Search" onChange={handleSearch} className="input input-bordered w-full max-w-xs" />
            <button className="btn btn-primary" onClick={handleOpenPermissionDialog}>{t("add") + t("space") + t("permission.name")}</button>
          </div>
          <div className="overflow-x-auto">
            <Table
              data={toTreeNode(tableData)}
              dataCount={0}
              onTableChange={onTableChange}
              isLoading={permissionFetcher.state !== "idle"}
              columns={[
                {
                  id: "id",
                  name: t("id"),
                  sortable: true,
                  indentable: true,
                },
                {
                  id: "name",
                  name: t("name"),
                  sortable: true,
                  indentable: true,
                },
                {
                  id: "zh_name",
                  name: t("zh_name"),
                  sortable: true,
                  indentable: true,
                },
                {
                  id: "type",
                  name: t("type"),
                },
                {
                  id: "path",
                  name: t("permission.path"),
                  sortable: true,
                },
                {
                  id: "icon",
                  name: t("icon"),
                  render: (node: TreeNode) => <i className={node.icon ? node.icon + " size-6 text-info" : ""}></i>,
                },
                {
                  id: "sort",
                  name: t("sort"),
                  sortable: true,
                },
                {
                  id: "action",
                  key: "id",
                  name: t("action"),
                  className: "min-w-24",
                  render: (node: TreeNode) => (
                    <>
                      <button className="btn btn-ghost btn-circle btn-sm" onClick={() => handleEditPermission(Number(node.id))}>
                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-5">
                          <path strokeLinecap="round" strokeLinejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
                        </svg>
                      </button>
                      <button className="btn btn-ghost btn-circle btn-sm" onClick={() => handleOpenDeletePermissionDialog(Number(node.id))}>
                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-5 text-error">
                          <path strokeLinecap="round" strokeLinejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
                        </svg>
                      </button>
                    </>
                  ),
                }
              ]} />
          </div>
        </div>
      </div>
      {/* dialog_delete_permission */}
      <dialog id="dialog_delete_permission" className="modal">
        <div className="modal-box">
          <span>{t("confirmDelete") + " [ " + findPermission(tableData, deletePermissionId)?.name + " ]"}</span>
          <Form method="post">
            <input type="hidden" name="id" value={deletePermissionId} />
            <div className="modal-action">
              <button className="btn btn-primary" name="action" value="delete">{t("confirm")}</button>
              <button className="btn" onClick={handleCloseDeletePermissionDialog}>{t("cancel")}</button>
            </div>
          </Form>
        </div>
      </dialog>
      {/* Add or Edit dialog */}
      <dialog id="dialog_permission" className="modal">
        <div className="modal-box max-w-3xl group">
          <h1 className="font-bold text-2xl text-center">{editPermissionInfo?.id ? t("edit") : t("add")} {t("permission.name")}</h1>
          <Form method="post">
            <input type="hidden" name="id" defaultValue={editPermissionInfo?.id ?? 0} />
            <input type="hidden" name="status" defaultValue={editPermissionInfo?.status ?? 0} />
            <input type="hidden" name="created_at" defaultValue={editPermissionInfo?.created_at?.toDateString()} />
            <input type="hidden" name="updated_at" defaultValue={editPermissionInfo?.updated_at?.toDateString()} />
            <div className="grid grid-cols-1 gap-x-5 sm:grid-cols-2">
              <label className="form-control w-full">
                <div className="label">
                  <span className="label-text after:content-['*'] after:ml-0.5 after:text-error">{t("name")}</span>
                </div>
                <input type="text" name="name" value={editPermissionInfo?.name ?? ""} onChange={handleFormChange} placeholder={t("name")} className={`input input-bordered w-full ${actionData?.name && "input-error"}`} />
                <FormFieldError msg={actionData?.name} />
              </label>
              <label className="form-control w-full">
                <div className="label">
                  <span className="label-text">{t("zh_name")}</span>
                </div>
                <input type="text" name="zh_name" value={editPermissionInfo?.zh_name ?? ""} onChange={handleFormChange} placeholder={t("zh_name")} className="input input-bordered w-full" />
              </label>
              <label className="form-control w-full">
                <div className="label">
                  <span className="label-text">{t("type")}</span>
                </div>
                <select name="type" value={editPermissionInfo?.type} onChange={handleFormChange} className="select select-bordered w-full">
                  <option value="1">{t("menu")}</option>
                  <option value="2">{t("button")}</option>
                </select>
              </label>
              <label className="form-control w-full">
                <div className="label">
                  <span className="label-text">{t("permission.path")}</span>
                </div>
                <input type="text" name="path" value={editPermissionInfo?.path ?? ""} onChange={handleFormChange} placeholder={t("permission.path")} className="input input-bordered w-full" />
              </label>
              {/* 此处用div替代label，为了避免label标签自动触发select聚焦，从而选disabled选项时也会自动关闭下拉菜单 */}
              <div className="form-control w-full">
                <div className="label">
                  <span className="label-text">{t("parent")}</span>
                </div>
                <input type="hidden" name="parent_id" value={editPermissionInfo?.parent_id ?? ""} />
                <TreeSelect
                  data={toTreeNode(treeSelectFetcher.data?.permissions ?? [])}
                  value={(editPermissionInfo?.parent_id ?? "").toString()}
                  currentId={(editPermissionInfo?.id ?? "").toString()}
                  onSelect={handleTreeSelect}
                  placeholder={t("pick_item")}
                  className="w-full" />
              </div>
              <label className="form-control">
                <div className="label">
                  <span className="label-text">{t("icon")}</span>
                </div>
                <input type="hidden" name="icon" value={editPermissionInfo?.icon ?? ""} />
                <IconSelect onSelect={handleIconSelect} value={editPermissionInfo?.icon ?? ""} />
              </label>
              <label className="form-control w-full">
                <div className="label">
                  <span className="label-text">{t("sort")}</span>
                </div>
                <input type="number" name="sort" value={editPermissionInfo?.sort ?? 0} onChange={handleFormChange} className="input input-bordered w-full" />
              </label>
            </div>

            <div className="modal-action">
              <button className={`btn btn-primary ${isSubmitting ? "btn-disabled" : ""}`}>
                {isSubmitting ? <span className="loading loading-spinner"></span> : ""}
                {t("save")}
              </button>
              <button className="btn" onClick={handleClosePermissionDialog}>{t("close")}</button>
            </div>
          </Form>
        </div>
      </dialog>
      <Notification notifications={notifications} />
    </>
  )
}

export async function action({ request }: ActionFunctionArgs) {
  const t = await i18nServer.getFixedT(request);

  const body = await request.formData();
  if (body.get("action") === "delete") {
    const r = await deletePermission(Number(body.get("id")));
    return {
      code: 0,
      message: r ? t("delete") + t("space") + t("success") : t("has_children"),
    }
  }

  const validate = zPermission.extend({
    name: z.string().min(1, { message: `${t("name")} ${t("required")}` }),
  })
  const data = Object.fromEntries(body);
  const result = validate.safeParse(data);
  if (!result.success) {
    const error = result.error.flatten();
    return {
      ...error.fieldErrors,
      code: 1,
      message: t("something_wrong"),
    }
  }

  const model = { ...result.data };
  model.id ? await updatePermission(model) : await createPermission(model);
  return {
    code: 0,
    message: (model.id ? t("edit") : t("add")) + t("space") + t("success"),
  }
}